package cn.zyjblogs.server.qrcode.service.impl;

import cn.zyjblogs.server.qrcode.constant.QrCodeEnum;
import cn.zyjblogs.server.qrcode.dto.QrCode;
import cn.zyjblogs.server.qrcode.service.QrCodeSerive;
import cn.zyjblogs.server.user.handler.OauthRquestHander;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.constant.CommonRedisKeyConstant;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.exception.AuthRuntimeException;
import cn.zyjblogs.starter.mybatisplus.utils.IdUtils;
import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class QrCodeSeriveImpl implements QrCodeSerive {
    private final TokenGranter tokenGranter;
    private final RedisTemplateHandler<String, QrCode> redisTemplateHandler;
    private final ClientDetailsService clientDetails;
    private final OAuth2RequestFactory oAuth2RequestFactory;

    public QrCodeSeriveImpl(AuthorizationServerEndpointsConfiguration authorizationServerEndpointsConfiguration,
                            ClientDetailsService clientDetails, RedisTemplateHandler redisTemplateHandler) {
        this.redisTemplateHandler = redisTemplateHandler;
        this.clientDetails = clientDetails;
        this.oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetails);
        this.tokenGranter = authorizationServerEndpointsConfiguration.getEndpointsConfigurer().getTokenGranter();
    }

    @Value("${zyjblogs.qrcode.redirect_url}")
    private String qrcodeRedirectUrl;


    /**
     * 创建验证码
     *
     * @param clientId
     * @return
     */
    @Override
    public QrCode createQrCode(String clientId) {
        if (clientId == null) {
            throw new AuthRuntimeException(HttpCode.BAD_REQUEST, "appid不能为空");
        }
        QrCode qrCode = QrCode.builder()
                .clienId(clientId)
                .code(getCode())
                .qrsig(IdUtils.nextUUID())
                .build();
        Map<String, String> query = new LinkedHashMap<>();
        query.put("k", qrCode.getQrsig());
        query.put("f", qrCode.getClienId());
        qrCode.setUrl(OauthRquestHander.append(qrcodeRedirectUrl, query, false));
        redisTemplateHandler.set(CommonRedisKeyConstant.getQrCodeRediKey(qrCode.getClienId(), qrCode.getQrsig()), qrCode, 3, TimeUnit.MINUTES);
        return qrCode;
    }

    public String getName(String qrsig) {
        return "qrcode/" + qrsig + "png";
    }

    public int getCode() {
        return RandomUtils.nextInt(10000, 99999);
    }


    /**
     * 检测验证码状态
     *
     * @param qrsig
     * @param clientId
     * @return
     */
    @Override
    public QrCode poll(String qrsig, String clientId) {
        String rediKey = CommonRedisKeyConstant.getQrCodeRediKey(clientId, qrsig);
        QrCode qrCode = redisTemplateHandler.get(rediKey);
        //已过期
        if (qrCode == null) {
            return QrCode.builder()
                    .code(getCode())
                    .message(QrCodeEnum.EXPIRE.getName())
                    .build();
        }
        //未扫码
        if (qrCode.getToken() == null) {
            qrCode.setMessage(QrCodeEnum.NO_SCAN_CODE.getName());
            return qrCode;
        }
        //已扫码
        if (QrCodeEnum.SCANNED_CODE.getName().equals(qrCode.getMessage())) {
            qrCode.setToken(null);
            return qrCode;
        }
        return qrCode;
    }

    /**
     * 进行扫码
     *
     * @param k qrsig
     * @param f clientId
     * @return
     */
    @Override
    public Boolean qrcodeScan(String k, String f) {
        QrCode qrCode = redisTemplateHandler.get(CommonRedisKeyConstant.getQrCodeRediKey(f, k));
        if (qrCode == null) {
            return false;
        }
        ClientDetails clientDetail = clientDetails.loadClientByClientId(f);
        if (clientDetail == null) {
            throw new AuthRuntimeException(HttpCode.BAD_REQUEST, "该appid不存在");
        }
        Map<String, String> parameters = new HashMap<>();
        parameters.put("grant_type", "qrcode");
        parameters.put("k", k);
        parameters.put("f", f);
        TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(parameters, clientDetail);
        OAuth2AccessToken token = tokenGranter.grant(tokenRequest.getGrantType(), tokenRequest);
        OAuth2AccessTokenVo oAuth2AccessTokenVo = OAuth2AccessTokenVo.TransferToken(token);
        qrCode.setMessage(QrCodeEnum.SCANNED_CODE.getName());
        qrCode.setToken(oAuth2AccessTokenVo);
        redisTemplateHandler.set(CommonRedisKeyConstant.getQrCodeRediKey(f, k), qrCode, 2, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public Boolean getQrCodeLogin(String qrsig, String clientId) {
        QrCode qrCode = redisTemplateHandler.get(CommonRedisKeyConstant.getQrCodeRediKey(clientId, qrsig));
        if (qrCode != null && qrCode.getToken() != null) {
            qrCode.setMessage(QrCodeEnum.LOGON.getName());
            redisTemplateHandler.set(CommonRedisKeyConstant.getQrCodeRediKey(clientId, qrsig), qrCode, 1, TimeUnit.MINUTES);
            return true;
        }
        return false;
    }
}
